﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using Engine.Sgml.Decoding;

namespace Engine.Sgml {
    internal sealed class UrlResolver {
        public UrlResolver() {
            Credentials = CredentialCache.DefaultCredentials;
            DefaultEncoding = Encoding.Default;
            }

        public ICredentials Credentials { get; set; }
        public Uri ResolvedUrl { get; private set; }
        public Encoding DefaultEncoding { get; set; }

        public static Uri ResolveUrl(Uri baseUrl, string relativeUrl) {
            Uri address = new Uri(relativeUrl, UriKind.RelativeOrAbsolute);
            if (address.IsAbsoluteUri)
                return address;
            if (baseUrl == null)
                throw new InvalidOperationException("Could not resolve URL because specified address is relative and base URL is null.");
            return new Uri(baseUrl, address);
            }

        public SgmlStream GetEntity(Uri absoluteUri) {
            string contentType;
            return GetEntity(absoluteUri, HttpRequestSettings.DefaultSettings, out contentType);
            }

        public SgmlStream GetEntity(Uri absoluteUri, HttpRequestSettings settings, out string contentType) {
            Guard.ArgumentNotNull(absoluteUri, "absoluteUri");
            if (!absoluteUri.IsAbsoluteUri)
                throw new ArgumentException("The Uniform resource identifier is not absolute.");
            Debug.Assert(DefaultEncoding != null, "The DefaultEncoding is null.");

            switch (absoluteUri.Scheme) {
                case "file":
                    ResolvedUrl = absoluteUri;
                    contentType = null;
                    return new SgmlStream(new FileStream(absoluteUri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read), DefaultEncoding);
                case "https":
                case "http":
                    HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(absoluteUri);
                    webRequest.UserAgent = settings.UserAgent;
                    webRequest.Timeout = settings.Timeout;
                    webRequest.Proxy = settings.WebProxy;
                    // Pass the cookie associated with request.
                    webRequest.CookieContainer = settings.CookieContainer;
                    webRequest.PreAuthenticate = false;
                    // Pass the credentials of the process. 
                    webRequest.Credentials = Credentials;

                    if (settings.Method.Equals("POST", StringComparison.OrdinalIgnoreCase)) {
                        webRequest.ContentType = settings.ContentType;
                        webRequest.Method = settings.Method;
                        byte[] byteArray = null;
                        if (settings.Content == null)
                            byteArray = new byte[0];
                        else
                            byteArray = DefaultEncoding.GetBytes(settings.Content);
                        Stream requestStream = webRequest.GetRequestStream();
                        requestStream.Write(byteArray, 0, byteArray.Length);
                        requestStream.Close();
                        }
                    WebResponse webResponse = webRequest.GetResponse();
                    contentType = webResponse.ContentType;
                    ResolvedUrl = webResponse.ResponseUri;
                    try {
                        Encoding encoding = Detector.DetectEncoding(webResponse.ContentType);
                        if (encoding == null)
                            encoding = DefaultEncoding;
                        return new SgmlStream(
                            webResponse.GetResponseStream(),
                            encoding
                            );
                        }
                    catch (SystemException e) {
                        throw new WebException("An error occurred while returns the data stream from Internet resource.", e);
                        }
                default:
                    throw new NotSupportedException(string.Format("The scheme '{0}' is not supported.", absoluteUri.Scheme));
                }
            }
        }
    }
